home *** CD-ROM | disk | FTP | other *** search
/ QuickTime 2.0 Developer Kit / QuickTime 2.0 Developer Kit.iso / mac / MAC / Programming Stuff / Macintosh Debugging / Blat / Blat.p < prev    next >
Encoding:
Text File  |  1992-08-07  |  67.1 KB  |  1,363 lines  |  [TEXT/MPS ]

  1. { Blat:
  2.     a dcmd to use the MMU to protect the low 256 bytes of RAM from stray use.
  3.     There is no data there, so any program writing (or reading) there is suspect.  This is an
  4.     even better, better bus error, since it will catch writes to 0, as well as reads from
  5.     zero, and anything up to 255.
  6.     Down side: You lose a bit of performance when this is installed.  I set the MMU table
  7.     size to be 256 bytes, and the extra overhead costs some performance.  Overall, the
  8.     machine is not noticeably slower.  I change VBR, install my own MMU tables.  These
  9.     are in use even when Blat is turned off.  I would leave it installed only when actively
  10.     testing, and remove it when not, just as a conservative approach.  This is a fairly weird
  11.     hunk of code, because it is so close to the machine.
  12.     
  13.     Caveats:
  14.         Sorry, but it won't work on a IIci, or IIsi.  The onboard video causes grief, and the
  15.             Ram is wildly mapped, even when it is in use.  Requires a special case to fix it.
  16.         The other bad thing is that it won't work on 040 machines.  The MMU is so different
  17.             that my tables won't work, so it would require another special case.
  18.     
  19.         
  20.     
  21.     Copyright © 1991 by Apple Computer, Inc., all rights reserved.
  22.  
  23.     by     Bo3b Johnson        8/28/91
  24.             MS 37-DS
  25.     for best viewing, use Palatino 12.
  26.     
  27.     8/28/91: First version of the program started from a skanky prototype.  Most
  28.                     of the really hard problems were solved there.
  29.     10/4/91: First real release to people.  Version 2.  Includes the Friedlander 
  30.                     interface additions, and tested on an fx and IIx.  Thanks, Jim, it's way
  31.                     easier to use now.
  32.     10/16/91: Version 3.  Fixed bug in 32 bit tables, that would map 1 Meg hunk at
  33.                         8 Meg and 9 Meg to the same place.  
  34.                     Fixed macsbug not being able to set breakpoints because I left protection
  35.                         on when I invoked it with DebugStr.  Now leave protection off.
  36.                     Changed table in 32 bit mode to have all DT=1 entries, since Microsoft Word
  37.                         had a bus error that was being handled by the system.  I had way high ram
  38.                         marked as invalid dt=0, for $1000 0000, and that made my MMU check fail
  39.                         in BusErrorGlue.  Now set dt=1, it is handled by a system bus error handler.
  40.                     Commented out starting ROM addresses, since they are fx specific.
  41.     10/23/91: Version 4: Fixed bug calling Gestalt early in boot, that would give 
  42.                         gestaltUndefSelectorErr, and I'd say VM was installed when it wasn't.  Now
  43.                         use extra AND to case on that error, and assume VM isn't installed if I get that.
  44.                     Added code to protect the VBR page too, so that I can bust Macsbug from taking
  45.                         over the bus error vector.  Check for VBR page validity too.
  46.                     Bumped up possible ignores to 200.
  47.     10/30/91: Version 5: Fixed bug introduced by locking the VBR page too, that of having 
  48.                         Macsbug not be able to install an A-trap handler.  Now allows all through
  49.                         except for writes to the bus error vector, which is saved in a global.  
  50.                     Finally forced to look for Macsbug active using the MacJmp flag, since at boot
  51.                         time Macsbug does its init stuff without having NMI blocked.
  52.                     Cleaned up a few variables and routines that weren't necessary anymore,
  53.                         because of the new 24-bit table reusing 32-bit table, and VBR installing in
  54.                         the system heap.
  55.                     Killed the Get/Set VBR/TC routines, since I do it all in one routine now that
  56.                         is called SetMMURegisters.  This is superior, since I can change the CRP and
  57.                         TC without turning off the MMU, which will be necessary for IIci someday.
  58. }
  59.  
  60. UNIT Blat;
  61.  
  62. {
  63.         Build this dude using the Build Menu, it has a make file.
  64.         
  65.         Things to do:
  66.             The registers are partly fried by the DebugStr for display.  Fixes?
  67.             Clean it up a bit.
  68.             Make it work in IIsi, IIci case.
  69.             Bigger page size matter on speed?
  70.             040 case of walking the tables to find bits to hit, spiffy bus error handler.             
  71.             
  72.             Seem to have trouble with macsbug Log still, get a hang, waiting in syncwait.
  73.             Incompatible with DAL?  Was problem on IIx, don't know why.
  74.             Not particularly pleased with the init code, it seems really ragged and should be
  75.                 modularized in a nicer form.
  76.             Maybe not save the bus error handler in a global, use 8 instead?  This would
  77.                 require turning off protection to get it.
  78.             Is VBR based handlers not being a chain OK?  ie. I force it to always be me.
  79.             
  80.                         
  81.         Questions:
  82.             Do I need to have a semaphore to prevent reentry?  I had one as an experiment in
  83.                 the prototype, but the bug turned out to be something else.  I've removed it here,
  84.                 under the assumption that it is unnecessary.  It was an attempt to prevent the
  85.                 handler from freezing Macsbug, by trying to break when Macsbug was invoked 
  86.                 to handle a real bus error.  I solved that by looking at NMIFlag instead.
  87.             Should I use the Macsbug flag instead of the NMIFlag to determine if Macsbug is
  88.                 running?  It's maybe more accurate, but it's in two places depending upon 24/32 bit
  89.                 mode.  Also, I'm pretty darn sure that NMIFlag is always set when Macsbug is
  90.                 running, to prevent NMIs inside of Macsbug. 
  91.             If I don't turn off the protection while you are in Macsbug, the user can't use or
  92.                 see that memory, but nothing bad happens.  Should I set it that way too, just
  93.                 for safety?  Currently it is always on when I invoke Macsbug.  If you set a 
  94.                 breakpoint, or break in some other way, it is still active too.
  95.             When the TC register disables the MMU, RAM is mapped straight through.  On a 
  96.                 IIci this would be bad for example, since the video is mapped at zero.  This should
  97.                 ensure that I won't get any bad jumps or anything while it is off.
  98.             On a IIci class machine, I could drive the installed table instead, and articulate the
  99.                 branch if need be.  This would enforce the system page size of 32K though, so 
  100.                 that needs some thought.  In the 040 case, the same is true, but need a really
  101.                 spiffy bus error handler.
  102.             Couldn't decide whether the bus error handler needs to check for the address
  103.                 being larger than 255, and calling the old one if so.  The way I fixed the Word
  104.                 problem was to make all pages valid so that the Invalid bit in the MMSR would
  105.                 not be set.  Not sure if that was the right way.
  106.             
  107.             
  108.         General:
  109.             OK, one big problem here is that the dcmd cannot turn on the MMU stuff when
  110.             it is called from Macsbug.  As a hang example, when the Bus Error handler calls
  111.             Macsbug via DebugStr, that starts up Macsbug.  The first thing Macsbug does is
  112.             try to change the bus error vector in low memory, so that it can handle its own
  113.             bus errors.  This invokes my bus error handler for a invalid write, where I call
  114.             Macsbug...  and you get the big wedge.  I fixed it partly by watching for the NMIFlag
  115.             to be on, which means that Macsbug is executing.   When it is executing, I don't 
  116.             report any references to the zero page, since any DebugStr while Macsbug is 
  117.             executing will cause a hang.  This was partly successful, but the next problem is
  118.             that Macsbug calls the video-driver to swap video depth.  That driver installs
  119.             a bus error handler temporarily to catch it killing itself I guess.  Anyway, this is
  120.             after Macsbug has installed its own bus error handler, so when the video driver
  121.             writes to $8, it invokes Macsbug's bus error handler, and that causes Macsbug to
  122.             hang again, as it tries to display a message before it is fully operational.  So....
  123.             This leads to the use of the VBR register to remap the vectors to a different 
  124.             location.  I currently put them in a block in the dcmd code space, which is way
  125.             high in memory, and a smaller target.  The trouble with this is that now any
  126.             of the system wide bus error handlers that are installed are being installed
  127.             zero based, not VBR based (the 68000 didn't have a VBR register, so the original
  128.             code had to use zero based, and no one has paid attention to VBR yet.)  Installed
  129.             there, my bus error handler still takes precedence, and I can pass along any
  130.             non-MMU error to the bus error handler at $8.  This is what VM does too.
  131.             I don't want to do it for all the vectors though, so any operations other than 
  132.             bus errors won't get fed through.
  133.             Too bad I guess.  There's nothing terribly important, but its something to note.
  134.             
  135.             OK, the definitive answer on whether I need to use VBR or not.  The answer is
  136.             yes, I must.  The reason is that if I set the zero page (0..255) as invalid, with DT=0,
  137.             then the cpu will halt with a double bus fault.  It takes an interrupt say, that
  138.             causes a fault, then it tries to get the bus error vector, which also causes a fault.
  139.             Biff, you've got a locked machine.  Ergo, I must use VBR, and it doesn't have
  140.             anything to do with Macsbug weirdness.  Currently, I shadow everthing up to the
  141.             VBR page, as the active vector page, and leave the zero page locked.  Both the 
  142.             zero page and the VBR page should be the same, except when some VBR
  143.             knowledgable code like Macsbug sneaks in and changes the VBR page.
  144.             
  145.             And, definitively, I must check for Macsbug running and avoid calling DebugStr
  146.             while it is active.  Macsbug itself is OK since it uses VBR based handlers.  It calls
  147.             the video driver which tries to write to 8, which forces a bus error which makes
  148.             Macsbug freak out with 'macsbug caused the exception'.  If Macsbug left it's handler
  149.             installed until after the video swap it would be OK, but it puts me back, then swaps
  150.             video, causing me to invoke macsbug while it is still active.  Hence, I put in the 
  151.             check for IsMacsbugActive to avoid the problem. 
  152.             
  153.             The other part of the story is that dcmd's can't set VBR, since Macsbug saves
  154.             and restores that value during it's switch.  So the dcmd changes the real, active
  155.             VBR, which is then crushed at Macsbug exit by the one saved in Macsbug's 
  156.             register file.  I can get around this by installing all my stuff at dcmd init time,
  157.             during boot.  This is not fully desireable since I really wanted to leave the 
  158.             machine in a more pristine state when Blat wasn't turned on.  The only real
  159.             difference will be that Blat installs it's tables and sets up VBR and the MMU
  160.             registers at dcmdInit, but leaves the protection itself turned off, so that the 
  161.             0 page is marked as valid instead of write or read protected.  This way the 
  162.             computer will use my tables during normal use, and turned on only when told
  163.             so by the dcmd.  The downside of this is that it will slow the computer by around
  164.             10%, since my tables are bigger than the systems (256 bytes/page, instead of 32k bytes).
  165.             This probably means you don't want to leave Blat installed when you aren't
  166.             actively using it, since it modifies the system at a fundamental level.  
  167.             
  168.             An experiment that I started was to try to see if I could make the system work
  169.             with a larger page size, like 4K.  The idea was to set up the bus error handler to
  170.             allow anything above 255 to go through, and only stop the 0..255 references.  The
  171.             test was to see if doing it that way with a bigger page would be slower or faster
  172.             than having the 256 byte page size.  The experiment failed in this environment,
  173.             since Macsbug uses low memory globals, and would thus generate an internal
  174.             bus error while it was running.  That would make it try to say 'Macsbug caused
  175.             the exception', which caused another bus error, and whammo you've got a well
  176.             hung machine.  I don't currently see any way around this, so I'm bagging the
  177.             experiment for now.
  178.             
  179.             The VBR based page is being used by the CPU while Blat is running.  This disables
  180.             any short term bus error handlers that are installed at zero, which is bad because
  181.             they are there to catch and handle weird errors, and to keep the system running.
  182.             I want those to still be active so that Blat doesn't disable some of this error fixing
  183.             code while it is installed.  Note this would happen even if Blat wasn't turned on,
  184.             since I have to install the VBR based table for reasons specified above (Macsbug
  185.             will hang).  So to avoid that problem and to make it more generally friendly, I
  186.             will turn on write protection from the start, when it is installed, and any time I
  187.             get a bus error I'll do the EmulateAccess so that that access will install into the
  188.             0 based page, as well as the VBR page.  By doing this I can keep the pages in sync,
  189.             so the system won't really notice that I'm doing a weird thing by moving VBR
  190.             from 0.  This doesn't give any safety to things since a bad access down there can
  191.             smoke the valid VBR table now, but in general the system runs smoothly, and
  192.             this is an informant type of tool instead of trying to enforce system rules.  In 
  193.             other words, if it previously crashed the computer, it still will; but now you can use
  194.             Blat to stop at invalid references so you can find the crashing bug immediately.
  195.             When Blat is turned on, that will just set a global that decides whether I should
  196.             stop or not, since the protection will already be on.  Blat is thus never really
  197.             off when it is installed.  It will just not show bus errors that it catches.   Another
  198.             side of this problem is that I cannot just copy up the temporary bus error handlers
  199.             that are installed while the system runs, since they would then be installed VBR
  200.             based, and when they went to de-install themselves at 0, an MMU induced bus
  201.             error would result, and their bus error handler would take it.  I can't expect them
  202.             to handle a bus error of this form, so as a hack I don't shadow writes to 8 up to
  203.             VBR+8, since those are bus error handlers.  This leaves my bus error handler in
  204.             charge up there at VBR+8.  Macsbug doesn't have a problem with this, since it
  205.             always installs its handler at VBR+8, and thus avoids writing to 8 and catching
  206.             the spurious errors.  When macsbug is executing, my bus error handler is 
  207.             not installed, so you can't write to the zero page, even with macsbug.
  208.             
  209.             In order to keep those temporary bus error handlers active and working like they
  210.             are supposed too, I need to pass along bus errors that aren't MMU related.  This
  211.             is for normal crash and burn type of bugs, that may be caught by a handler.  The
  212.             system is full of these temp install things, and I need to keep them working as
  213.             expected, or the stability of the system with blat installed would be worse.  In order
  214.             to do this, when I take a bus error in my handler, I look to see if it is MMU related
  215.             by doing a PTEST instruction.  If that says it's an MMU error, then I'll go ahead
  216.             and do the normal blat thing to see if I stop or let it go through.  If it isn't an
  217.             MMU induced bus error, then I just feed the error on to the handler that is 
  218.             installed at 8.  That will be the current handler, and presumably knows how to 
  219.             handle the error.  It can also call up the debugger in the case where it shouldn't
  220.             have happened.   Thus temporary handlers are available still, and the system
  221.             should run as it normally does, except a bit slower due to the table overhead and
  222.             the bus errors that don't cause stops.
  223.             
  224.             An interesting problem I ran into on a different machine was that the IIfx will
  225.             run properly even if the RAM is in the wrong bank.  The machine I looked at
  226.             had 16 Meg in 4 Meg simms, but they were installed in Bank B instead of in
  227.             Bank A as is customary.  The ROM is smart enough to set up MMU page tables
  228.             to map all the RAM back to memory location 0 from 128Meg where Bank B 
  229.             starts.  This poses quite the problem for me however, since the RAM is
  230.             physically installed way up there.  When I turn off the MMU to remap it for
  231.             my tables, the machine crashes, of course, since RAM is now 128 Meg away, and
  232.             the PC is executing funny space.  So... in case it doesn't work on your IIfx, look
  233.             to see if your RAM is installed in Bank B instead, will make it impossible for
  234.             me to run.  (I don't think there is a solution to the problem, other than having
  235.             real RAM in Bank A).  As long as there is some RAM in Bank A, I should be fine.
  236.             
  237.             SwapMMUMode will flush the cpu caches when it changes mode.  Specifically I
  238.             don't need to do that, because the addresses and data that are cached are still 
  239.             valid after I set a new CRP with new tables.  I don't change the mapping like
  240.             SwapMMUMode, I only change the table types.  
  241.             
  242.             Wow, what a surprise, another totally gnarly problem to try to solve.  The problem
  243.             occurs when protection is on, and you break into macsbug.  Macsbug is then running
  244.             with protection (either read or write) turned on, and anything that happens will
  245.             make Macsbug barf.  Macsbug has installed it's own bus error handler into the VBR
  246.             based table, upon entry, as a way of catching errors and restarting it's 'Main Event
  247.             Loop'.  The trouble is, when you do something like Log, or even switching to the
  248.             regular mac screen, some system code runs, which tries to install another bus
  249.             error handler, zero based.  That is busted by still on protection, and the current
  250.             bus error handler being Macsbug-- gives the technicolor yawn.  This doesn't have
  251.             anything to do with my leaving the protection on from boot on up, since it will
  252.             happen whenever the protection is active, like when Blat is doing its job.  Waah.
  253.             It's too hard.  OK, so what I'm doing now is to try to lock down the bus error
  254.             handler so that only I have control over the handler, and can't be replaced by
  255.             other code, most specifically, Macsbug.  I need to have a VBR based set of vectors,
  256.             since I still need to allow for read protection, and you can't do read protection on
  257.             the active vectors, as noted above.  I can however do write protection on the VBR
  258.             based vectors, which is what I'm doing to prevent Macsbug from coming in and
  259.             stomping my vector.  I need to take control first, and allow things to go on through
  260.             as if I wasn't there, whenever Macsbug is active.  This will prevent the type of 
  261.             bug where Macsbug catches a bus error that I should be handling.  I'll still keep
  262.             track of the current secondary bus error handler, and call it if it isn't an MMU
  263.             protection error.  Motorola really blew it by overloading the bus error vector.  This
  264.             would have been easy if they'd made a specific MMU bus error vector.  Now, in
  265.             order to protect the VBR vectors too, I need to have an entry in my tables to 
  266.             set the WP bit on.  I don't want to change the tables too much, since they are hard
  267.             to debug, so I'll make the restriction that the VBR page has to be allocated in the
  268.             system heap in the first 64K, which is how far my table stretches for the fourth 
  269.             level in 32 bit mode.  That's 256 entries of 256 bytes apiece.  If I can allocate a block
  270.             of Ram there, then I can align it to a page boundary, and set the WP bit, preventing
  271.             anyone from changing it from underneath of me.  
  272.             
  273.             As a sidelight, I thought about a different way to solve that problem, which was to
  274.             try to catch whenever Macsbug would be activated, and turn off protection during
  275.             those times.  Most things go through MacJmp, so I could theoretically install my
  276.             own MacJmp, turn it off, then fly on to macsbug, coming back, turn it back on, then
  277.             back to the rom.  I chose not to do it this way, because Macsbug has more than one
  278.             entry vector.  It will use the trace bit and the a-trap handler too, on occasion, making
  279.             it further difficult.  If I didn't trap those and whatever else I don't know about, then
  280.             you could get into macsbug with the protection still active, and get the same problems
  281.             as before.  It would work mostly, but still have problems.  
  282.             
  283.             A convenient modification I made to the tables made it unneccessary to have a full
  284.             4 levels on the 24-bit tables.  I set up the third level in those tables to be the same as
  285.             the 4th level for the 32-bit tables.  That set of tables is an 8-bit hunk, so there are 256
  286.             entries in the table, and that is mapping 256 byte page sizes so it works in both 24 and
  287.             32 bit mode.  A side effect of this is that I no longer need to keep two places to turn
  288.             protection off or on, since the same table is used in both modes.  Now I just save the
  289.             single address, and set the byte to yea/nay.  I still have the VBR table too, since that
  290.             was a new addition, and it is in that 8-bit table as well, but wherever it was allocated
  291.             in Ram.
  292.             
  293.             I realized that I had done a dumb thing here, which was not setting up a chain of
  294.             standard bus error handlers.  I was saving the bus error handler off in a global, which
  295.             of course meant I could only save a single handler, and any subsequent reads of 
  296.             memory location 8 would get the default Rom bomb handler.  This was clearly a 
  297.             mistake as I found on the emulator, and so now I go ahead and just write the value
  298.             on through to memory location 8, save it in a global, and any time I get a bus error 
  299.             that needs to feed through, then I'll just pass it along to that address.  This effectively
  300.             makes a chain of handlers, since the save/restore characteristic of other code in the
  301.             system will read what's there, save it, then set it's own, then restore the old.  If 
  302.             something in between does its own handler installation, it will read the prior level
  303.             and everything will be fine.  It will be a chain maintained in the locals of the code
  304.             installing the temporary handlers.  
  305.             
  306.             Sorry about the kMaxIgnores hard coded number for the number of PCs to ignore.
  307.             Its one of those funny things that's hard to get around in a dcmd.  You have to 
  308.             assume that the dcmd is invoked later at interrupt time, since that is how an NMI
  309.             will get into macsbug.  This means the heaps are in an unknown state, so you 
  310.             cannot legitimately allocate memory.  You can work most of the time, but that's not
  311.             good enough.  This means it's really hard to have dynamic memory allocation in a
  312.             dcmd.  The simplest answer is to have a constant and just allocate it upfront during
  313.             dcmdInit, when it IS safe to allocate memory.  To be more polite, I should get a 
  314.             constant from a mx?? macsbug resource and thus allow it to be changed using resedit.
  315.             Course, this is one of the main reasons I give out the source, so you can change the
  316.             numbers and recompile easily.
  317. }
  318.  
  319. {$R-}
  320. {$D+}        { debug labels on. }
  321.  
  322. INTERFACE
  323.  
  324.         USES MemTypes, Resources, Traps, Memory, ToolUtils, OSUtils, Events, SysEqu,
  325.                     GestaltEqu, dcmd;                                            { Macsbug interface routines. }
  326.  
  327.         
  328.                     { Lower case starting letters means I don't use that identifier in the record. 
  329.                         This frame is generated by the CPU on bus errors. }
  330. TYPE        BusErrorFrame = Record
  331.                     SR: Integer;
  332.                     PC: LongInt;
  333.                         typeAndVector: Integer;
  334.                         internal1: Integer;
  335.                     SSW: Integer;                                                { Special Status Word, info bits. }
  336.                         pipeC: Integer;
  337.                         pipeB: Integer;
  338.                     FaultAddress: Ptr;                                        { Address that caused bus error. }
  339.                         internal2: Array [1..2] of Integer;
  340.                     OutputBuffer: LongInt;                                { Get bytes from here, for a write. }
  341.                         internal3: Array [1..4] of Integer;
  342.                         stageBaddress: LongInt;
  343.                         internal4: Array [1..2] of Integer;
  344.                     InputBuffer: LongInt;                                { Stuff bytes here, on a read. }
  345.                         internal5: Array [1..3] of Integer;
  346.                         version: Integer;
  347.                         internal6: Array [1..18] of Integer;
  348.                 End;
  349.                 BusErrorFramePtr = ^BusErrorFrame;        { So I don't copy the record. }
  350.                 
  351.                     { For the CRP and TC combinations in both 24 and 32 bit mode, I make a record
  352.                         so that I can explicitly define their order in RAM.  This is necessary because
  353.                         I install a pointer to these records (as global vars in the dcmd space), and that
  354.                         pointer is used by the system during SwapMMUMode.  It must be the
  355.                         CRP register followed by the TC register. }
  356.                 MMURegisters = Record
  357.                     theCRP: Int64Bit;
  358.                     theTC: LongInt;
  359.                 End;
  360.                 
  361.                     { When I pass back hex numbers on the stack, I want to use small ones. }
  362.                 Str8 = String[8];
  363.                 
  364.     { Vars that are used in other units, or outside of this unit need to be declared in the
  365.         Interface section.  This is a genuine global, so it is prefixed by the 'g'.  The Z+ option
  366.         says to make these available to the Linker, so that the Asm side of the world can
  367.         see the global var. }
  368.         
  369.         {$PUSH} {$Z+}
  370. VAR    { *** make access methods so I don't have to do this. }
  371.                 gSavedSR: Integer;                            { Save place for TurnInterruptsOff. }
  372.                 gStandardBusError: LongInt;        { save off the currently installed bus error vector. }
  373.                 gStandardVBRBusError: LongInt;    { save off the currently installed VR bus error vector. }
  374.         {$POP}
  375.  
  376.             { Public declaration for dcmdGlue. Must be in every dcmd. The name cannot be changed. }
  377.         PROCEDURE CommandEntry (paramPtr: dcmdBlockPtr);
  378.  
  379.             { This must be exported so that the assembly side can find this routine. }
  380.         PROCEDURE BusError (theFrame: BusErrorFramePtr);
  381.  
  382. IMPLEMENTATION
  383.  
  384.     CONST
  385.                 kShortDT = 2;                                    { For use as DT=2. }
  386.                 kProtectionOff = $01;                        { DT=1 for valid page, no protection. }
  387.                 kWriteProtect = $05;                        { DT=1, but the WP bit is set. }
  388.                 kReadWriteProtect = $00;                { DT=0, so the page is marked invalid. }
  389.                 
  390.                 kMMU24Info = MMUTbl;            { make the better name from include file. }
  391.                 kMMU32Info = MMUTblSize;    { 32 bit MMU table pointer in low memory.}
  392.                 
  393.                 kMaxIgnores = 200;                            { Maximum number of addresses to ignore. }
  394.                 
  395.                 kHexDigits = '0123456789ABCDEF';        { Digits in base 16, for hex conversion. }
  396.                 kUseIL =  False;
  397.                 kUseIP =  True;
  398.                 kOn = True;
  399.                 kOff = False;
  400.                 
  401.         { Some don't
  402.             really have to be globals, but it is a convenience.  I label them with a p, so that you can
  403.             immediately see they are private globals (to this unit), a quality MacApp convention.
  404.             I use more globals than normal in dcmd's since Macsbug has such a wimpy stack. }
  405.     VAR    pDumpString: Str255;                    { To dump label info, from symbols in code. }
  406.     
  407.                 pRegisters24: MMURegisters;        { 24 bit CRP and TC values I install. }
  408.                 pRegisters32: MMURegisters;        { 32 bit CRP and TC values I install. }
  409.  
  410.                 pProtectAddress: Ptr;                        { byte that turns protection on and off. }
  411.                 pProtectValue: Byte;                        { current setting of Blat. }
  412.                 
  413.                 pIgnoreArray: Array [1..kMaxIgnores] of LongInt;    { pc addresses to ignore as valid bus errors. }
  414.                 pIgnoreCount: Integer;                    { Number of valid ignore addresses. }
  415.                 pLearn: Boolean;                                { is learn mode on, or not}
  416.                 pAutoLearn: Boolean;                    {autolearn is silent, doesn't break into MacsBug}
  417.                 pUseIP: Boolean;                                { do we do IL or IP when we break}
  418.                 
  419.                 pDisplayError: Boolean;                { Blat On/Off:  to show bus errors or not. }
  420.                 
  421.                 pErrorString: Str255;                        { Error string if I can't install on the machine. }
  422.                 
  423.                 pVBRTable: Ptr;                                { table allocated in system heap for VBR vectors. }
  424.                 pVBRProtectAddress: Ptr;                { Entry in tables for VBR page in system heap. }
  425.                 
  426. {---------------------------------------------------------------------------------------------------------------------------------}
  427.         
  428.         { Not really external as procedures, they are table data.  This gives us a nice handy
  429.             Pascal interface to get the address of each table, without having to pretend that
  430.             the tables are code. }
  431. FUNCTION  GetStartSpace: Ptr;    EXTERNAL;
  432.  
  433. FUNCTION  GetFirstLevel32: Ptr;    EXTERNAL;
  434. FUNCTION  GetSecondLevel32: Ptr;    EXTERNAL;
  435. FUNCTION  GetThirdLevel32: Ptr;    EXTERNAL;
  436. FUNCTION  GetFourthLevel32: Ptr;    EXTERNAL;
  437.  
  438. FUNCTION  GetFirstLevel24: Ptr;    EXTERNAL;
  439. FUNCTION  GetSecondLevel24: Ptr;    EXTERNAL;
  440.  
  441. FUNCTION  GetEndOfTables: Ptr;    EXTERNAL;
  442.  
  443. PROCEDURE SaveTheA5;     EXTERNAL;
  444. PROCEDURE SetVBR (vbrPointer: Ptr);    EXTERNAL;
  445. FUNCTION GetVBR: LongInt;    EXTERNAL;
  446. PROCEDURE FlushATC (address: Ptr);    EXTERNAL;
  447.  
  448.  
  449. PROCEDURE SetMMURegisters (theRegisters: MMURegisters); EXTERNAL;
  450.  
  451. PROCEDURE TurnInterruptsOff;    EXTERNAL;
  452. PROCEDURE TurnInterruptsOn;    EXTERNAL;
  453.  
  454. PROCEDURE BusErrorGlue;    EXTERNAL;
  455.  
  456. FUNCTION FindMMUEntry (address: Ptr): Ptr;    EXTERNAL;
  457.  
  458. {---------------------------------------------------------------------------------------------------------------------------------}
  459.     { Well, I stole this routine from MacApp utilities.  I want to lower case the strings so I 
  460.         don't have case sensitivities.  This will do it, without using the toolbox. }
  461. PROCEDURE LowerStr255(VAR s: Str255);
  462.  
  463. VAR    i:    INTEGER;
  464.  
  465. BEGIN
  466.     FOR i := 1 TO LENGTH(s) DO
  467.         IF (s[i] IN ['A'..'Z']) THEN
  468.             s[i] := CHR(Ord(s[i]) + 32)
  469. END;        { LowerStr255 }
  470.  
  471.  
  472. {---------------------------------------------------------------------------------------------------------------------------------}
  473.     { Another handy routine stolen from MacApp to do the conversion on the dang strings.  I
  474.         only pass back Str8, since that is the maximum length, and stack space is limited in 
  475.         Macsbug, and I don't want to waste it needlessly.  
  476.         Notably, this one handles negative LongInts properly, unlike the one distributed with
  477.         the dcmd samples. }
  478. FUNCTION NumberToHex(decNumber: UNIV LongInt): Str8;
  479.  
  480. VAR    i: Integer;
  481.             hexNumber: Str8;
  482.  
  483. BEGIN
  484.     hexNumber[0] := CHR(8);
  485.     FOR i := 8 DOWNTO 1 DO
  486.         BEGIN
  487.             hexNumber[i] := kHexDigits[BAND(decNumber, 15) + 1];
  488.             decNumber := BSR(decNumber, 4);
  489.         END;
  490.     NumberToHex := hexNumber;
  491. END;
  492.  
  493.  
  494. {---------------------------------------------------------------------------------------------------------------------------------}
  495.     { A utility routine to decide if I'm in 32 bit mode or not.  This has to check the low
  496.         memory global to see if the bit is set. }
  497. FUNCTION  Is32BitMode: Boolean;
  498.  
  499. VAR            bitMode: Byte;                                                    { For convenient bit testing. }
  500.  
  501. BEGIN
  502.     bitMode := Ptr(MMU32Bit)^;                                        { Get MMU32bit value. }
  503.     IF BTst (bitMode , 0) THEN 
  504.         Is32BitMode := True
  505.     ELSE
  506.         Is32BitMode := False;
  507. END;
  508.  
  509. {---------------------------------------------------------------------------------------------------------------------------------}
  510.     { A utility routine to decide if Macsbug is running or not.  If it is, I can't call things like
  511.         DebugStr.  This checks the low memory global NMIValue, since Macsbug sets it when
  512.         it is entered.  A potentially better choice is the MacJmp top byte which has flags to say
  513.         a similar thing, but it is in two places, depending on bit mode. 
  514.         I've got the sort of reverse logic here because I wanted to make it easier to read 
  515.         where I use it.
  516.         I have to set it up the way Macsbug turns itself on, since NMIFlag by itself is not
  517.         good enough.  At boot, Macsbug is active, but hasn't set it.  There may be other times
  518.         as well.  I going to be conservative here, and just check both fields to see if it might
  519.         active, on the assumption that it is safer, and has no drawback. }
  520. FUNCTION  MacsbugIsNotActive: Boolean;
  521.  
  522. CONST    MacJmpFlag = $BFF;                                                { Flags only defined in assembly. }
  523.                 MacJmp = $120;                                                        { Used in different bit mode. }
  524.                 kMacsbugActive = 7;                                                { Bit in flags as debugger running. }
  525.                 
  526. VAR        NMIValue: Byte;
  527.                 MacsbugFlags: Byte;
  528.                 Rom24Only: Boolean;
  529.  
  530. BEGIN
  531.     NMIValue := Ptr(NMIFlag)^;
  532.     IF  BTST (NMIValue, 7) THEN                                        { Bit set means Macsbug is live. }
  533.         MacsbugIsNotActive := False                                        { And only do that check. }
  534.     ELSE BEGIN
  535.             { The NMIFlag is not set, so check the secondary characteristic of the MacJmp flags. 
  536.                 If the machine is in 32 bit mode, use the MacJmpFlag to see.  If it is in 24 bit mode, 
  537.                 the flags are stored in the high byte of the MacJmp vector.  God I love this compatibility
  538.                 programming.  The MacJmpFlag is worse than that even.  It turns out that it is $FF
  539.                 if the ROM is not capable of 32 bit mode.  If it is capable, it is used as the flags, 
  540.                 regardless of the bit mode.  Ugh.  This is how Macsbug checks, and since this is here
  541.                 strictly for Macsbug compatibility, I am doing it the same way.  Sorry about the
  542.                 -1 as a magic number, but it isn't defined as a constant, it IS a magic number as 
  543.                 used by Macsbug and the header files. }
  544.         Rom24Only := Ptr(MacJmpFlag)^ = -1;
  545.         IF Rom24Only THEN
  546.             MacsbugFlags := Ptr(MacJmp)^
  547.         ELSE 
  548.             MacsbugFlags := Ptr(MacJmpFlag)^;
  549.             
  550.         IF NOT BTST (MacsbugFlags, kMacsbugActive) THEN
  551.             MacsbugIsNotActive := True
  552.         ELSE
  553.             MacsbugIsNotActive := False;
  554.     END;
  555. END;
  556.  
  557. {---------------------------------------------------------------------------------------------------------------------------------}
  558.     { A routine to set the bytes for protection.  This one byte is found in the 24 bit and 32
  559.         bit MMU page tables.  The same set of tables are used in both bit modes, with different
  560.         TC values.  This just sets the DT byte of that short format entries to be the
  561.         protection specified in the global.  The actual memory location is set up when the tables
  562.         are initialized at InstallTables. The FlushATC is necessarly since the Address Translation
  563.         Cache in the MMU will have cached the protection state for that page, and I just changed
  564.         it.  For the VBR page, I want to turn it back on, but it can never be kReadWrite or it's death.}
  565. PROCEDURE TurnProtectionOn;
  566.  
  567. BEGIN
  568.     pProtectAddress^ := pProtectValue;                                    { restore the protection value. }
  569.     pVBRProtectAddress^ := kWriteProtect;                            { Keep it from changing in VBR. }
  570.     FlushATC (NIL);
  571.     FlushATC (pVBRTable);                                                        { Flush zero page, and VBR page. }
  572. END;
  573.  
  574. {---------------------------------------------------------------------------------------------------------------------------------}
  575.     { A routine to set the bytes for protection.  This does the counterpart of TurnProtectionOn,
  576.         but of course disables the MMU protection for the benefit of my routines, primarily the
  577.         EmulateAccess routine.  I have to turn it off during my writes, to avoid getting a 
  578.         reentrant bus error.  Naturally this just sets the DT=01 as a valid descriptor with no
  579.         write protect.  Turn off protection for the VBR based page too. }
  580. PROCEDURE TurnProtectionOff;
  581.  
  582. BEGIN
  583.     pProtectAddress^ := kProtectionOff;                
  584.     pVBRProtectAddress^ := kProtectionOff;    
  585.     FlushATC (NIL);
  586.     FlushATC (pVBRTable);                                                        { Flush zero page, and VBR page. }
  587. END;
  588.  
  589. {---------------------------------------------------------------------------------------------------------------------------------}
  590.     { When called upon by the user, this routine will ignore the address that is passed in, by
  591.         adding it to the ignores list; then bumping the ignore count.  These addresses are
  592.         skipped during bus error handling, as valid accesses. }
  593. PROCEDURE AddToIgnores (ignoreAddress: LongInt);
  594.  
  595. BEGIN
  596.     pIgnoreCount := pIgnoreCount + 1;
  597.     IF pIgnoreCount > kMaxIgnores Then 
  598.         dcmdDrawLine (ConCat(' too many addresses to ignore, max: ', NumberToHex(kMaxIgnores)))
  599.     ELSE BEGIN
  600.         pIgnoreArray [pIgnoreCount] := ignoreAddress;
  601.         IF pAutoLearn = kOn THEN
  602.             dcmdDrawLine (ConCat(' Auto-learning address: ', NumberToHex (ignoreAddress)))
  603.         ELSE IF pLearn = kOn THEN
  604.             dcmdDrawLine (ConCat(' Learning address: ', NumberToHex (ignoreAddress)))
  605.         ELSE
  606.             dcmdDrawLine (ConCat(' Ignoring address: ', NumberToHex (ignoreAddress)));
  607.     END;
  608. END;
  609.  
  610. {---------------------------------------------------------------------------------------------------------------------------------}
  611.     { Dump the list of ignored values, so they can see what all is being ignored. }
  612. PROCEDURE DumpIgnoresList;
  613.  
  614. Var        I: Integer;
  615.  
  616. BEGIN
  617.     dcmdDrawLine (' Blat list of ignored addresses: ');
  618.     For I := 1 to pIgnoreCount Do 
  619.         dcmdDrawLine (ConCat('   ', NumberToHex (pIgnoreArray[I])));
  620. End;
  621.  
  622. {---------------------------------------------------------------------------------------------------------------------------------}
  623.  
  624.     { When set up and running, I take bus errors that are due to the memory protection
  625.         I've installed.  This routine is the actual bus error handler, that is installed.  It does
  626.         several things, since Motorola blew it and overloaded the bus error routine.  (eg. page
  627.         faults should go through a different vector.)  It will examine the frame to see if it is one
  628.         generated by the MMU, and if so, will clear the re-run bit, to say I'll handle it.  The 
  629.         page protection is turned off, the memory access is emulated, then the protection is
  630.         turned back on, and the routine returns with an RTE to continue. }
  631.  
  632.  
  633.     { EmulateAccess is to run the memory operation the CPU was trying to do when I busted
  634.         it's chops by having the page protected.  This is presumably a legitimate access to the
  635.         zero page vectors, like you might see when the ROM installs a one-shot bus error
  636.         handler.  Emulate the access by figuring out the address, the data to move, the size,
  637.         and which way to move it.   All this info is in the bus error stack frame.  The 
  638.         faultAddress is the memory location that caused the bus error, either as a read or
  639.         as a write.   theSSW is the Special Status Word, which has the flags describing the
  640.         access.  The outputBuffer is the right aligned data that is being written.  The inputBuffer
  641.         is only used when it is a read operation, and is a pointer to the location in the stack
  642.         frame that needs the data. 
  643.         
  644.         As part of making the machine run properly, since this has to be installed at boot, I
  645.         want to allow the access to go through to both the 0 based access, as well as the VBR
  646.         based table, so that short term bus error handlers are installed in the active place off
  647.         of VBR.  Nearly all the accesses are valid, and in any case, this is just trying to be a
  648.         spy tool, not a police tool.  Doing it this way makes ADB key work again, and any
  649.         other funky system junk that need the vector page (like sound, via hardware).  I 
  650.         could do a PTEST and pass the bus error on to the 0 based page, but that would 
  651.         special case the bus errors, and my goal is really just to inform the programmer,
  652.         which will have already been done.  For writes then, I'll write the data to both 
  653.         the zero based access that I caught, and will write it to VBR+offset.  For reads, I'll
  654.         just let it do the normal thing of getting it from zero, since VBR based vectors and
  655.         0 based vectors should always be in sync. 
  656.         
  657.         More complication, of course.  Since I now protect the VBR based page from writes,
  658.         I can take bus errors there too, primarily from Macsbug.  In fact, anything other than
  659.         a write to vbr+8 is probably a bug, so I'll not even emulate those.  If it is VBR+8, then
  660.         I need to save the address, for passing along in case of a real bus error, but I still won't 
  661.         write it to the VBR page. }
  662. PROCEDURE EmulateAccess (theSSW: Integer; faultAddress: Ptr;
  663.                                          outputBuffer: Ptr; inputBuffer: Ptr);
  664.                                          
  665. CONST        kReadBit = 6;
  666.                     kByteBit = 4;                                                    { If set, always a byte access. }
  667.                     kWordBit = 5;                                                { If both 4&5 are clear, it's a long access. }
  668.                     kLong = 0;
  669.                     kWord = 1;
  670.                     kByte = 2;                                                        { constants for a Case. }
  671.         
  672. VAR            source, destination: Ptr;
  673.                     vbrDestination: Ptr;                                    { for copying it there too. }
  674.                     doRead: Boolean;
  675.                     theSize: Integer;                                            { Set up for a Case. }
  676.                     shadow: Boolean;
  677.                 
  678. BEGIN
  679.         { Right up top, if this is an access above the 0..255, then it must be the VBR page only,
  680.             since I don't want to shadow backwards from vbr to 0 based. } 
  681.     IF ORD(faultAddress) > 255 THEN
  682.         shadow := False
  683.     ELSE
  684.         shadow := True;
  685.         
  686.         { Set up a couple of local vars that are easier to deal with than the BitTest stuff. }
  687.     doRead := BTst (theSSW, kReadBit);
  688.     
  689.     IF BTst (theSSW, kByteBit) Then                            { Set up theSize. }
  690.         theSize := kByte
  691.     ELSE IF BTst (theSSW, kWordBit) Then
  692.         theSize := kWord
  693.     ELSE theSize := kLong;                                                { I'm ignoring the 3-byte case. }
  694.     
  695.         { Now decide which of the two types it is, read or write.  For Reads, I need to move
  696.             the data from the fault address to the InputBuffer, which is in the stack frame itself.
  697.             I have to move the data in a right-aligned fashion though, so for anything less
  698.             than a long word, I need to bump up the destination pointer so it will be right
  699.             aligned data.  Then I need to move only the amount of data specified, since I
  700.             could bus error again, for addresses on the edge. }
  701.     IF doRead THEN BEGIN
  702.         source := faultAddress;                                            { For reads, go from fault to inputBuffer. }
  703.         destination := inputBuffer;
  704.         Case theSize Of
  705.             kByte:    BEGIN
  706.                 destination := Ptr(ORD(destination) + 3);    { Bump pointer by 3 for right aligned. }
  707.                 destination^ := source^;                                    { Move it only as a byte. }
  708.             END;
  709.             kWord: BEGIN
  710.                 destination := Ptr(ORD(destination) + 2);    { Bump pointer by 2 for right aligned. }
  711.                 IntegerPtr(destination)^ := IntegerPtr(source)^;
  712.             END;
  713.             kLong: BEGIN
  714.                 LongIntPtr(destination)^ := LongIntPtr(source)^;    { Move full 4 bytes across. }
  715.             END;
  716.         END;    { Case theSize }
  717.     END
  718.         
  719.         { For Write operations, I have the source data in the output buffer that is on the 
  720.             stack frame.  That data needs to go to the fault address.  The data in the output buffer
  721.             is right aligned, so I need to bump the source pointer for everything except the
  722.             long word access.  Also, only move the exact size of data specified. 
  723.             I added the write to VBR based page too. }
  724.     ELSE BEGIN
  725.         source := outputBuffer;                                            { For writes, move it from output to fault. }
  726.         destination := faultAddress;
  727.         vbrDestination := Ptr(ORD(faultAddress) + GetVBR); { make address in VBR based page. }
  728.  
  729.         Case theSize Of
  730.             kByte:    BEGIN
  731.                 source := Ptr(ORD(source) + 3);                    { Bump pointer by 3 for right aligned. }
  732.                 destination^ := source^;                                    { Move it only as a byte. }
  733.                 IF shadow THEN vbrDestination^ := source^;        { Move byte to VBR page too. }
  734.             END;
  735.             kWord: BEGIN
  736.                 source := Ptr(ORD(source) + 2);                    { Bump pointer by 2 for right aligned. }
  737.                 IntegerPtr(destination)^ := IntegerPtr(source)^;
  738.                 IF shadow THEN IntegerPtr(vbrDestination)^ := IntegerPtr(source)^;
  739.             END;
  740.             kLong: BEGIN
  741.                     { Don't copy new bus error handlers up there. }
  742.                 IF ORD(destination) = ORD(pVBRTable)+BusErrVct THEN
  743.                     gStandardVBRBusError := LongIntPtr(source)^
  744.                 ELSE  IF ORD(destination) = BusErrVct THEN BEGIN
  745.                         gStandardBusError := LongIntPtr(source)^;
  746.                         LongIntPtr(destination)^ := LongIntPtr(source)^;    { and into low memory too. }
  747.                     END
  748.                 ELSE BEGIN
  749.                     LongIntPtr(destination)^ := LongIntPtr(source)^;    { Move full 4 bytes across. }
  750.                     IF shadow THEN LongIntPtr(vbrDestination)^ := LongIntPtr(source)^;
  751.                 END;
  752.             END;
  753.         END;    { Case theSize }
  754.     END;
  755. END;        { EmulateAccess }
  756.  
  757.  
  758. {---------------------------------------------------------------------------------------------------------------------------------}
  759.     { This bus error handler will take the interrupts as they come in, and the procedure 
  760.         definition is set up to use the stack frame left on the stack, as it is entered.
  761.         This is fairly weird, but I wanted to
  762.         see how far I could push the high-level language use.  There are actually two frame
  763.         types I am concerned about, the $A and $B frames, and they are different lengths.
  764.         I've defined the stack parameters as the larger frame, and then when the smaller
  765.         frame is there, I won't use the extended fields.  The routine exits by an assembly 
  766.         routine anyway, so the frame won't need to get stripped off the stack.  I have to
  767.         do an RTE to restart the CPU, and that's just too dang hard to do from up here.
  768.         I put the small routines into the .a file instead of doing Inline code, even though
  769.         I get extra JSRes.  This isn't run often so the easier to read Asm file is the higher
  770.         priority. }
  771.         
  772.     { Since theFrame is bigger than 4 bytes, it will be passed by address, and the assembly
  773.         glue set it up that way.  When this routine is executed, the BusErrorGlue has been
  774.         run, which has saved the scratch registers, turned off interrupts, and set up A5 to
  775.         be the dcmd's A5.   theFrame is passed in as a pointer, so that Pascal won't make a 
  776.         local copy of the entire record as a local.   The BusErrorGlue just passes the pointer.
  777.         
  778.         Can't just save and restore the protection byte, it may be changed while in the Debug
  779.         Str, to a new value. }
  780. PROCEDURE BusError (theFrame: BusErrorFramePtr);
  781.  
  782. VAR        doDisplay: Boolean;
  783.                 I: Integer;
  784.  
  785. BEGIN
  786.         { Got here from BusErrorGlue, and have saved registers, restored A5, and bailed
  787.             if it wasn't an MMU related bus error. }
  788.  
  789.         { If Macsbug is currently executing, then I don't want to call DebugStr, since that will
  790.             cause a hang; Macsbug isn't reentrant.  Also, since I don't want to stop if they
  791.             haven't turned Blat on, I need to check that global.  I will still take bus errors
  792.             in order to keep the VBR page up to date. }
  793.     IF  MacsbugIsNotActive & pDisplayError  THEN BEGIN    
  794.  
  795.             { Loop for all the ignore addresses, to see if any match. }
  796.         doDisplay := True;
  797.         FOR I := 1 TO pIgnoreCount  DO
  798.             IF pIgnoreArray[I] = theFrame^.PC THEN BEGIN
  799.                 doDisplay := False;
  800.                 Leave;                                                                            { If one matched, skip out of loop. }
  801.             END;
  802.             
  803.             { If there was no matching address, this one needs to be displayed. }
  804.         IF doDisplay THEN BEGIN
  805.             {always draw stuff, even if in Auto-learn mode, we will only break if not in auto mode}
  806.             dcmdDrawLine('===========================');
  807.             dcmdDrawLine ('--Blat Bus Error: ');
  808.             dcmdDrawLine (ConCat('--address: ', NumberToHex (theFrame^.FaultAddress)));
  809.             
  810.             {jf - if learn or auto-learn is on, add it to the learn list, let the user know if autoLearn is on}
  811.             IF ((pLearn = kOn) OR (pAutoLearn = kOn)) THEN
  812.                 AddToIgnores(theFrame^.PC);
  813.  
  814.  
  815.             {only break into MacsBug if auto-learn is off}
  816.             IF pAutoLearn = kOff THEN BEGIN
  817.                 IF pUseIP = kUseIP THEN
  818.                     DebugStr (ConCat(';ip ', NumberToHex (theFrame^.PC)))
  819.                 ELSE
  820.                     DebugStr (ConCat(';il ', NumberToHex (theFrame^.PC)));
  821.             END;
  822.  
  823.         End;
  824.     End;    { Not in Macsbug and needed to stop. }
  825.         
  826.         { Clear the rerun bit in the Special Status Word, so that when the RTE is hit, it
  827.             won't try to rerun the bus access.  Since I'm emulating the access, I have already
  828.             cleared the bus error condition, so there's no need to rerun it.  This mask is just
  829.             to clear the 8th bit in the word. }
  830.     theFrame^.SSW := BAnd (theFrame^.SSW, $FEFF);
  831.     
  832.         { Turn off the memory protection since I'm going to do the emulate access anyway,
  833.             and it has to be off to keep this from bus erroring.  }
  834.     TurnProtectionOff;
  835.     
  836.     With theFrame^ DO
  837.         EmulateAccess(SSW, FaultAddress, @OutputBuffer, @InputBuffer);
  838.     
  839.     TurnProtectionOn;                                                { Restore to write protect, or read protect. }
  840. End;        { BusError }
  841.  
  842.  
  843. {---------------------------------------------------------------------------------------------------------------------------------}
  844.     { Verify that the machine is one that I can run on without wedging.  For example, I don't
  845.         run on IIci, or 040 machines, so I gotta say no here, instead of pounding. 
  846.         Checks for IIci on board video class machine,
  847.          no 040 machines,
  848.          no VM running. }
  849. FUNCTION VerifyMachine: Boolean;
  850.  
  851. VAR     err: Integer;
  852.             response: LongInt;
  853.     
  854.         { Sure, this is like a Goto, but it should be obvious how it works.  This is the way 
  855.             out of here when there is an error, and sets the error string in a common way.  I
  856.             prefer this error routine style to massively nested If Then Else structures. }
  857.     PROCEDURE ErrorExit (theErr: Str255);
  858.     BEGIN
  859.         VerifyMachine := False;                                                    { Return failing result. }
  860.         pErrorString := theErr;
  861.         Exit (VerifyMachine);                                                        { Jump out of enclosing routine. }
  862.     END;
  863.     
  864. BEGIN
  865.         { See if the VBR vector page made it inside of the 64K max that my tables allow. }
  866.     IF (pVBRTable = NIL) | (ORD(pVBRTable) > $10000) THEN
  867.         ErrorExit ('  System heap too large, VBR allocation failed.');
  868.  
  869.         { If VM is installed, or we can't get info on it, set that as an error string. }
  870.     err := Gestalt(gestaltVMAttr,  response);
  871.  
  872.         { Allow through gestaltUndefSelectorErr, as not a real Gestalt error. }
  873.     IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN 
  874.         ErrorExit ('  Gestalt error');
  875.         
  876.         { If gestaltUndefSelectorErr Then VM isn't installed.  Test bit if valid result. }
  877.     IF ((err = noErr) & BTst(response, gestaltVMPresent)) THEN
  878.         ErrorExit ('  Blat is incompatible with VM');
  879.  
  880.     
  881.         { If this is an 040 machine, then complain about that.  If the selector is undefined,
  882.             I have to assume this isn't an 040. }
  883.     err := Gestalt(gestaltProcessorType,  response); 
  884.     IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN 
  885.         ErrorExit ('  Gestalt error');
  886.     IF ((err = noErr) & (response = gestalt68040)) THEN
  887.         ErrorExit ('  Blat is incompatible with the 68040 MMU.');
  888.     
  889.         { If you don't have an MMU, clearly this isn't going to work.  If I get an undefined
  890.             selector error, I have to assume one isn't installed. }
  891.     err := Gestalt(gestaltMMUType,  response); 
  892.     IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN 
  893.         ErrorExit ('  Gestalt error');
  894.     IF ((err = gestaltUndefSelectorErr) | (response = gestaltNoMMU)) THEN
  895.         ErrorExit ('  Blat requires an MMU in order to function.');
  896.     
  897.         { If this is one of the IIci class machines, with onboard video, I can't run, since their
  898.             tables are too complicated for me.  If I get an undefined gestalt selector here,
  899.             something is really screwed up. }
  900.     err := Gestalt(gestaltMachineType,  response); 
  901.     IF (err <> noErr) THEN 
  902.         ErrorExit ('  Gestalt error');
  903.     IF ((response = gestaltMacIIci) | (response = gestaltMacIIsi) | (response = gestaltMacLC)) THEN
  904.         ErrorExit ('  Blat cannot run on machines that support onboard video (IIci, IIsi, LC).');
  905.             
  906.         { Finally, if I exit normally, then the string will be emptied, since it looks like we I run. }
  907.     pErrorString := '';            
  908.     VerifyMachine := True;
  909. END;        { VerifyMachine }
  910.  
  911. {---------------------------------------------------------------------------------------------------------------------------------}
  912.     { Save and install the new bus error handler for the system.  This is my bus error handler
  913.         which will handle all the MMU faults, and feed through the errors to the old handler.
  914.         The old handler is maintained in the gStandardBusError as I take writes to 8. }
  915. PROCEDURE InstallBusErrorHandler;
  916.  
  917. VAR    vbrLand: LongIntPtr;
  918.  
  919. BEGIN
  920.         { Save off the current bus error handler in 8, to init our var.   Init the
  921.             VBR based one to point at my handler, since that is the normal case. }
  922.     gStandardBusError := LongIntPtr(BusErrVct)^;
  923.     gStandardVBRBusError := ORD(@BusErrorGlue);
  924.         
  925.         { Install a new bus error handler into the vector page, to point to my routine instead. 
  926.             By installing after the VBR is set up to high dcmd area, I install my bus error
  927.             handler only at the VBR vector table.  If a bus error comes through that isn't 
  928.             related to memory protection, it gets fed on through to the vector that is 0 based. }
  929.     vbrLand := LongIntPtr(ORD(GetVBR)+BusErrVct);
  930.     LongIntPtr(vbrLand)^ := ORD(@BusErrorGlue);
  931.     
  932. END;        { InstallBusErrorHandler }
  933.  
  934. {---------------------------------------------------------------------------------------------------------------------------------}
  935.     { Set up the VBR based vector table, and set that table as the new VBR in active use.  
  936.         The fundamental reason for doing this is so that I can set Read protection on the 0
  937.         based version of the vectors.  VBR use makes it possible to survive that. }
  938. PROCEDURE InstallVBR;
  939.  
  940. BEGIN
  941.  
  942.         { Round up the address in pVBRTable so that it is page aligned (low byte zero), and thus
  943.             will be at a 256 byte page boundary.  Copy the vectors from zero up to there, to
  944.             start the shadow copy of vectors.  This is where I will set the
  945.             VBR register to point, so it will be the real vectors in use.  The bytes at zero will
  946.             thus no longer be used for the vectors. }
  947.     pVBRTable := Ptr(ORD(pVBRTable) + 256);
  948.     pVBRTable := Ptr(BAND (ORD(pVBRTable), $FFFFFF00));
  949.     BlockMove (NIL, pVBRTable, 256);
  950.     SetVBR (pVBRTable);
  951.  
  952. END;        { InstallVBR }
  953.  
  954.  
  955. {---------------------------------------------------------------------------------------------------------------------------------}
  956.  
  957.     { When the user asks to install, this routine will take the globals saved off, and install
  958.         them as the current MMU tables.  By setting up the CRP register to be my tables, that
  959.         installs them and the proper entry is marked as invalid. }
  960. PROCEDURE InstallTables;
  961.  
  962. TYPE    Int64BitPtr = ^Int64Bit;
  963.  
  964. VAR    crpPointer: Int64BitPtr;
  965.             tcPointer: LongIntPtr;
  966. VAR     xx: SignedByte;
  967.  
  968. BEGIN    
  969.         { While fooling with the MMU registers, I want to avoid screwing up any interrupt
  970.             routines that expect Ram to be mapped when they run.   No interrupts while changing. }
  971.         
  972.         { Set the Translation Control register to 0, to disable the MMU.  This has to be done to
  973.             allow a change to the CRP.  Next change the Cpu Root Pointer to be our new tables,
  974.             that are installed in the code.  After that, restore the TC to its new value, that matches
  975.             the new tables, and is enabled.  When it turns back on the Address Translation Cache
  976.             will be flushed automatically by the MMU. }
  977.     IF  Is32BitMode Then SetMMURegisters (pRegisters32)
  978.     ELSE  SetMMURegisters (pRegisters24);
  979.     
  980.         { Install the two register values at the MMU24Info in low memory, for SwapMMUMode. 
  981.             MMUTbl is the old name for MMU24Info, but is the only one defined in the interface. 
  982.             I made my own constant that is the better name. 
  983.             This just installs a new pointer to the variables I use, since on a IIx, they point into 
  984.             ROM to start with, and I can't just change the values.  By setting them to point to
  985.             me in RAM, the IIx will just use those numbers and not reset them.  The order
  986.             of them is important, which is why I made them into a record. }
  987.     LongIntPtr(kMMU24Info)^ := ORD(@pRegisters24);
  988.     LongIntPtr(kMMU32Info)^ := ORD(@pRegisters32);
  989.             
  990.         { *** debug operation to find it easily on emulator. }
  991.     xx := Ptr(5)^;
  992.     
  993.  
  994.         { Find the protection address for the VBR table itself.  Bump up by three, to make it a
  995.             byte access for the TurnProtectionOff/On. }
  996.             {*** should do a FindMMUEntry for the zero page too, to be symmetric. }
  997.     pVBRProtectAddress := FindMMUEntry (pVBRTable);
  998.     pVBRProtectAddress := Ptr(ORD(pVBRProtectAddress) + 3);
  999. END;        { InstallTables }
  1000.  
  1001.  
  1002. {---------------------------------------------------------------------------------------------------------------------------------}
  1003.     { Patch up the MMU tables, to make them fit for MMU use.  They have to be quad-long
  1004.         word aligned, and the various stages of the tables have to point to the next lower
  1005.         leaves in the tree.  The 3rd level of the 24 bit table will use the 4th level of the 32
  1006.         bit table, since it is specifiying the same Ram. }
  1007. PROCEDURE PatchupTables;
  1008.  
  1009. VAR        adjustment: LongInt;
  1010.                 count: LongInt;
  1011.                 tableLand: LongIntPtr;
  1012.                 firstLevel: Ptr;                                    { pointer to the FirstLevel table in dcmd code space. }
  1013.  
  1014. BEGIN
  1015.         { A block in the assembly file has the MMU tables.  This code will move the tables
  1016.             in memory lower by however many bytes it takes to make it quad-long word aligned.  It
  1017.             does this by starting at the StartSpace and fudging that address higher by however many
  1018.             bytes it takes to make a quad-long aligned address.  I do this by adding 16 to the number,
  1019.             then clearing the low nibble with the AND.  This is rounding up to the next highest
  1020.             quad-word boundary.  A 16 byte section of zeros is at the start of the tables, to absorb
  1021.             the move. }
  1022.     count := ORD(GetEndOfTables) - ORD(GetFirstLevel32);
  1023.     firstLevel := Ptr(ORD(GetStartSpace) + 16);
  1024.     firstLevel := Ptr(BAND (ORD(firstLevel), $FFFFFFF0));
  1025.     BlockMove (GetFirstLevel32, firstLevel, count);
  1026.  
  1027.         { Now screw around with the tables, making them all point to the successive levels,
  1028.             as the MMU requires.  The bottom nibble of each entry is the DT field as specified 
  1029.             by the MMU stuff, and I always make it DT=2, for short entries.  This makes the
  1030.             first entry of the FirstLevel table point to the SecondLevel, and the first entry of
  1031.             the SecondLevel table point to the ThirdLevel, and so on.  I have to subtract out
  1032.             the adjustment for each, since I moved all the tables with BlockMove above. }
  1033.     adjustment := ORD(GetFirstLevel32) - ORD(firstLevel);            { how far each table moved. }
  1034.     
  1035.     tableLand := LongIntPtr(ORD(GetFirstLevel32) - adjustment);
  1036.     tableLand^ := BOR(ORD(GetSecondLevel32) - adjustment, kShortDT);
  1037.     
  1038.     tableLand := LongIntPtr(ORD(GetSecondLevel32) - adjustment);
  1039.     tableLand^ := BOR(ORD(GetThirdLevel32) - adjustment, kShortDT);
  1040.  
  1041.     tableLand := LongIntPtr(ORD(GetThirdLevel32) - adjustment);
  1042.     tableLand^ := BOR(ORD(GetFourthLevel32) - adjustment, kShortDT);
  1043.  
  1044.         { Modify the tables for 24-bit mode too, so that they are correct. }
  1045.     tableLand := LongIntPtr(ORD(GetFirstLevel24) - adjustment);
  1046.     tableLand^ := BOR(ORD(GetSecondLevel24) - adjustment, kShortDT);
  1047.     
  1048.         { Set the pointer to the third level to point to the same table that is used for
  1049.             the fourth level of the 32-bit mode tables. }
  1050.     tableLand := LongIntPtr(ORD(GetSecondLevel24) - adjustment);
  1051.     tableLand^ := BOR(ORD(GetFourthLevel32) - adjustment, kShortDT);
  1052.  
  1053.         { Save off the address of the low byte of the first longInt entry for the FourthLevel,
  1054.             since that is the spot that turns the protection on and off.  This byte is the DT value
  1055.             for that page, and with the WP set, will write protect that first page in memory. }
  1056.     pProtectAddress := Ptr(ORD(GetFourthLevel32) - adjustment + 3);
  1057.     
  1058.         { Set up our new CRP number to match that table address.  This means writing in the
  1059.             write number for the top half which is the table index stuff, which is disabled in my
  1060.             case.  The low word of that is DT=2 which means short entries for the next level, 
  1061.             which is pointed at by the low long.  The CRP is a double longint. }
  1062.     firstLevel := Ptr(ORD(GetFirstLevel24) - adjustment);
  1063.     pRegisters24.theCRP.hiLong := $7FFF0002;                { index limit turned off, DT=2 }
  1064.     pRegisters24.theCRP.loLong := ORD(firstLevel);    { pointer to first level tables. }
  1065.     
  1066.     firstLevel := Ptr(ORD(GetFirstLevel32) - adjustment);
  1067.     pRegisters32.theCRP.hiLong := $7FFF0002;                { index limit turned off, DT=2 }
  1068.     pRegisters32.theCRP.loLong := ORD(firstLevel);    { pointer to first level tables. }
  1069.     
  1070.         { Set up the new TC register, that will be used when my tables are installed.  For
  1071.             24-bit mode it's like: 8=enabled, 0=supervisor root pointer and function code
  1072.             lookup are disabled, 8=256 byte page size, 8=initial shift of 8 bits, 4=TIA for 4
  1073.             bits in the first table, 4=TIB, 8=TIC, 0=TID.
  1074.             32-bit mode:  the top 8=enabled, 0=supervisor root pointer and function 
  1075.             code lookup are disabled, 8=256 byte page size, 0=initial shift is zero bits (32-bit
  1076.             mode), 5=TIA bits for first level table, 7=TIB, 4=TIC, 8=TID. }
  1077.     pRegisters24.theTC := $80884480;
  1078.     pRegisters32.theTC := $80805748;
  1079.  
  1080. END;        { PatchupTables }
  1081.  
  1082.  
  1083. {---------------------------------------------------------------------------------------------------------------------------------}
  1084.     { This installation routine will allocate a block in the system heap for the MMU tables that
  1085.         I use to replace the system tables.  It will install a bus error handler, and allocate a new
  1086.         block for the actual vectors, making them VBR based instead of zero based.  This last
  1087.         bit is to make it work with Macsbug, and make things more reliable. }
  1088. PROCEDURE CreateBlat;
  1089.  
  1090. BEGIN
  1091.         { Allocate a VBR table in the system heap.  It has to be within the first 64K or my
  1092.             tables won't map to it nicely.  If it doesn't show up in that range, then barf.  The
  1093.             +256 is to allow for the roundoff operation of making sure it is allocated on an
  1094.             exact 256 byte page boundary.  VBR doesn't have to be that way, but I'm allocating
  1095.             it there so I can protect the page, and that makes it necessary to align it to a page. }
  1096.     pVBRTable := NewPtrSys(256+256);
  1097.     
  1098.         { Just to be nice, make sure that we can run on this machine, without wedging.  If it
  1099.             isn't going to work, then skip the install.  Looks at pVBRTable validity too. }
  1100.     IF NOT VerifyMachine THEN Exit (CreateBlat);
  1101.     
  1102.         { Make the tables all cool.  Fix up the base address, patch in the multiple levels. }    
  1103.     PatchupTables;
  1104.     
  1105.         { Save off the current A5, so that it can be used when the bus error handler is called. }
  1106.     SaveTheA5;
  1107.  
  1108.     pIgnoreCount := 0;
  1109.     
  1110.         { Addresses are only good on IIfx. }
  1111. (*        Only good on fx, and you can get them with Auto anyway. 
  1112.  
  1113.         { *** set up a hack list of memory addresses to ignore. }
  1114.     pIgnoreArray [1] := $4080DB76;                                    { Heapguts }
  1115.     pIgnoreArray [2] := $4080DB98;                                    { Heapguts }
  1116.     pIgnoreArray [3] := $4080DC04;                                    { Heapguts }
  1117.     pIgnoreArray [4] := $4080DC26;                                    { Heapguts }
  1118.     pIgnoreArray [5] := $4080DBF8;                                    { Heapguts, read }
  1119.     pIgnoreArray [6] := $4080DB6A;                                    { Heapguts, read }
  1120.     pIgnoreArray [7] := $40808D14;                                        
  1121.     pIgnoreArray [8] := $40808D20;                                        
  1122.     pIgnoreArray [9] := $40808D30;                                        
  1123.     pIgnoreArray [10] := $4080C982;                                        
  1124.     pIgnoreCount := 10;
  1125. *)
  1126.  
  1127.         {jf stuff follows}
  1128.     pLearn := kOff;
  1129.     pAutoLearn := kOff;
  1130.     pUseIP := kUseIP;
  1131.  
  1132.         { Install the new VBR tables, my bus error handle, and my tables as the active MMU tables. }
  1133.     InstallVBR;
  1134.     InstallBusErrorHandler;
  1135.     InstallTables;
  1136.  
  1137.         { Stuff the starting value for the protection.  This is normally set to kWriteProtect, so
  1138.             that all writes can be shadowed in the VBR table to keep things running.   Start out
  1139.             with not displaying the bus errors. }
  1140.     pProtectValue := kWriteProtect;
  1141.     pDisplayError := False;
  1142.     TurnProtectionOn;                                                            { Turn on protect bytes. }
  1143.     
  1144. END;        { CreateBlat }
  1145.  
  1146.  
  1147. {---------------------------------------------------------------------------------------------------------------------------------}
  1148.     { When debugging is required, as it often seems to be, maybe this will help, by
  1149.         coughing up all the global variables, and some addresses for runtime inspection.  
  1150.         This particular dcmd cannot be run inside the TestDcmd world. }
  1151. PROCEDURE BlatDebug;
  1152.  
  1153. BEGIN
  1154.     dcmdDrawLine ('--Blat Debugging Info--');
  1155.  
  1156.     dcmdDrawLine (ConCat(' Code Start->: ', NumberToHex (@LowerStr255)));
  1157.     dcmdDrawLine (ConCat(' gStandardBusError: ', NumberToHex (gStandardBusError)));
  1158.     dcmdDrawLine (ConCat(' gStandardVBRBusError: ', NumberToHex (gStandardVBRBusError)));
  1159.     dcmdDrawLine (ConCat(' gSavedSR: ', NumberToHex (gSavedSR)));
  1160.     dcmdDrawLine (ConCat(' pRegisters24.theCRP: ', NumberToHex (pRegisters24.theCRP.hiLong), NumberToHex (pRegisters24.theCRP.loLong)));
  1161.     dcmdDrawLine (ConCat(' pRegisters24.theTC: ', NumberToHex (pRegisters24.theTC)));
  1162.     dcmdDrawLine (ConCat(' pRegisters32.theCRP: ', NumberToHex (pRegisters32.theCRP.hiLong), NumberToHex (pRegisters32.theCRP.loLong)));
  1163.     dcmdDrawLine (ConCat(' pRegisters32.theTC: ', NumberToHex (pRegisters32.theTC)));
  1164.     dcmdDrawLine (ConCat(' pProtectAddress: ', NumberToHex (ORD(pProtectAddress))));
  1165.     dcmdDrawLine (ConCat(' pProtectValue: ', NumberToHex (pProtectValue)));
  1166.     dcmdDrawLine (ConCat(' pVBRTable: ', NumberToHex (ORD(pVBRTable))));
  1167.     dcmdDrawLine (ConCat(' pVBRProtectAddress: ', NumberToHex (pVBRProtectAddress)));
  1168.     dcmdDrawLine (ConCat(' pErrorString: ', pErrorString));
  1169.     dcmdDrawLine (ConCat(' pIgnoreCount: ', NumberToHex (pIgnoreCount)));
  1170.     DumpIgnoresList;
  1171.  
  1172.     IF pDisplayError THEN
  1173.         dcmdDrawLine (' pDisplayError: True')
  1174.     ELSE
  1175.         dcmdDrawLine (' pDisplayError: False');
  1176.     IF pLearn THEN
  1177.         dcmdDrawLine (' pLearn: True')
  1178.     ELSE
  1179.         dcmdDrawLine (' pLearn: False');
  1180.     IF pAutoLearn THEN
  1181.         dcmdDrawLine (' pAutoLearn: True')
  1182.     ELSE
  1183.         dcmdDrawLine (' pAutoLearn: False');
  1184.     IF pUseIP THEN
  1185.         dcmdDrawLine (' pUseIP: True')
  1186.     ELSE
  1187.         dcmdDrawLine (' pUseIP: False');
  1188.     IF Is32BitMode THEN
  1189.         dcmdDrawLine (' In 32 Bit Mode')
  1190.     ELSE
  1191.         dcmdDrawLine (' In 24 Bit Mode');
  1192.     IF MacsbugIsNotActive THEN
  1193.         dcmdDrawLine (' Macsbug is Not Active')
  1194.     ELSE
  1195.         dcmdDrawLine (' Macsbug is Active');
  1196. END;
  1197.  
  1198. {---------------------------------------------------------------------------------------------------------------------------------}
  1199.     { dump the current settings of blat. }
  1200. PROCEDURE DumpStatusInfo;
  1201.  
  1202. BEGIN
  1203.     dcmdDrawLine('-------------------------------------');
  1204.     IF NOT pDisplayError THEN
  1205.         dcmdDrawLine (' Blat is OFF')
  1206.     ELSE IF pProtectValue = kWriteProtect THEN
  1207.         dcmdDrawLine (' Blat is catching WRITES only')
  1208.     ELSE IF pProtectValue = kReadWriteProtect THEN
  1209.         dcmdDrawLine (' Blat is catching both READS and WRITES');
  1210.  
  1211.     IF pLearn = kOn THEN
  1212.             dcmdDrawLine (' Blat learning mode is ON')
  1213.     ELSE
  1214.             dcmdDrawLine (' Blat learning mode is OFF');
  1215.             
  1216.     IF pAutoLearn = kOn THEN
  1217.             dcmdDrawLine (' Blat auto-learning mode is ON')
  1218.     ELSE
  1219.             dcmdDrawLine (' Blat auto-learning mode is OFF');
  1220.             
  1221.     IF pUseIP = kUseIP THEN
  1222.             dcmdDrawLine (' Blat is using IP')
  1223.     ELSE
  1224.             dcmdDrawLine (' Blat is using IL');
  1225.     dcmdDrawLine('-------------------------------------');
  1226.  
  1227. END;
  1228.  
  1229. {---------------------------------------------------------------------------------------------------------------------------------}
  1230.     { If something was not understood, dump the info for how this works.  If I couldn't install
  1231.         because of an incompatibility, then say that instead, and include the error message so
  1232.         they know why. }
  1233. PROCEDURE DumpHelpInfo;
  1234.  
  1235. BEGIN
  1236.     IF pErrorString = '' THEN BEGIN
  1237.             dcmdDrawLine ('Blat   Off | Writes | Both | Dump | Ignore $xxx ... | IP | IL | Learn | Auto | Status');
  1238.             dcmdDrawLine ('   Uses the MMU to catch accesses to bytes 0..255.  (Version 5)');
  1239.             DumpStatusInfo;
  1240.         END
  1241.     ELSE BEGIN
  1242.             dcmdDrawLine ('Blat is NOT installed.');
  1243.             dcmdDrawLine (pErrorString);                            { Error line if not installed. }
  1244.         END;
  1245. END;
  1246.  
  1247. {---------------------------------------------------------------------------------------------------------------------------------}
  1248. {---------------------------------------------------------------------------------------------------------------------------------}
  1249.  
  1250.  
  1251.     { This fine fellow is the main entry point for the dcmd.  It is the hook by which I get called
  1252.         by MacsBug to do my thing.  It is basically the chance to key off the command line and do
  1253.         what they request. 
  1254.         
  1255.         Change the version number in the help, whenever it is re-released.   This is the only
  1256.         version number in the program. }
  1257. PROCEDURE CommandEntry (paramPtr: DCmdBlockPtr);
  1258.  
  1259. VAR     parseChar:              CHAR;
  1260.             ignoreDude:         LongInt;
  1261.             okParse:                 Boolean;
  1262.                 
  1263. BEGIN
  1264.     CASE    paramPtr^.request OF
  1265.             { When I'm called to Init, do the init code of setting up the MMU tables. }
  1266.         dcmdInit: 
  1267.                 CreateBlat;
  1268.                 
  1269.             { I can get various DoIt commands, so parse out the options.   If I don't get anything,
  1270.                 do the standard dump help info.  I lowercase the string so I can avoid any case sensitivity
  1271.                 on options passed in. }
  1272.         dcmdDoIt:    
  1273.             BEGIN
  1274.                 parseChar := dcmdGetNextParameter (pDumpString);
  1275.                 LowerStr255 (pDumpString);
  1276.                 
  1277.                 IF pDumpString = 'writes' THEN BEGIN
  1278.                     pDisplayError := True;                                            { Stop on errors, to show them. }
  1279.                     pProtectValue := kWriteProtect;
  1280.                     TurnProtectionOn;                                                    { Set bytes to Write Protect. }
  1281.                     dcmdDrawLine (' Blat will catch all writes to memory from 0..255.');
  1282.                 END
  1283.                 
  1284.                 ELSE IF pDumpString = 'both' THEN BEGIN
  1285.                     pDisplayError := True;                                            { Stop on errors, to show them. }
  1286.                     pProtectValue := kReadWriteProtect;
  1287.                     TurnProtectionOn;                                                    { Set bytes to Invalid (R/W protect). }
  1288.                     dcmdDrawLine (' Blat will catch both reads and writes to memory from 0..255.');
  1289.                 END
  1290.                 
  1291.                 ELSE  IF pDumpString = 'off' THEN BEGIN
  1292.                     pDisplayError := False;                                            { don't stop on bus errors. }
  1293.                     pProtectValue := kWriteProtect;
  1294.                     TurnProtectionOn;                                                    { Protection still on, to maintain VBR.}
  1295.                     dcmdDrawLine (' Blat is off');
  1296.                 END
  1297.                 
  1298.                 ELSE  IF pDumpString = 'dump' THEN BEGIN
  1299.                     DumpIgnoresList;
  1300.                 END
  1301.                 
  1302.     {jf stuff follows}
  1303.                 ELSE  IF pDumpString = 'ip' THEN BEGIN
  1304.                     dcmdDrawLine (' Blat will use IP');
  1305.                     pUseIP := kUseIP;
  1306.                 END
  1307.                 
  1308.                 ELSE  IF pDumpString = 'il' THEN BEGIN
  1309.                     dcmdDrawLine (' Blat will use IL');
  1310.                     pUseIP := kUseIL;
  1311.                 END
  1312.                 
  1313.                 ELSE  IF pDumpString = 'learn' THEN BEGIN
  1314.                     IF pLearn = kOn THEN BEGIN
  1315.                         pLearn := kOff;
  1316.                         dcmdDrawLine(' Blat learn mode OFF');
  1317.                     END
  1318.                     ELSE BEGIN
  1319.                         pLearn := kOn;
  1320.                         pAutoLearn := kOff;
  1321.                         dcmdDrawLine(' Blat learn mode ON (auto-learn is OFF)');
  1322.                     END;
  1323.                 END
  1324.                 
  1325.                 ELSE  IF pDumpString = 'auto' THEN BEGIN
  1326.                     IF pAutoLearn = kOn THEN BEGIN
  1327.                         pAutoLearn := kOff;
  1328.                         dcmdDrawLine(' Blat auto-learn mode OFF');
  1329.                     END
  1330.                     ELSE BEGIN
  1331.                         pAutoLearn := kOn;
  1332.                         pLearn:= kOff;
  1333.                         dcmdDrawLine(' Blat auto-learn mode ON (learn is OFF)');
  1334.                     END;
  1335.                 END
  1336.                 
  1337.                 ELSE  IF pDumpString = 'ignore' THEN BEGIN
  1338.                     REPEAT  {loop through and get all of the addresses to ignore}
  1339.                         parseChar := dcmdGetNextExpression (ignoreDude, okParse);
  1340.                         IF okParse THEN AddToIgnores (ignoreDude);
  1341.                     UNTIL not okParse;
  1342.                 END
  1343.                 
  1344.                 ELSE IF pDumpString = 'status' THEN
  1345.                     DumpHelpInfo
  1346.  
  1347.                 ELSE  IF pDumpString = 'debug' THEN BEGIN
  1348.                     BlatDebug;
  1349.                 END
  1350.                 
  1351.                 ELSE    DumpHelpInfo;
  1352.             END;
  1353.             
  1354.             { Give them the obvious help info. }
  1355.         OTHERWISE
  1356.                 DumpHelpInfo;
  1357.                 
  1358.     END;        { End of case paramPtr^.request. }
  1359.         
  1360. END;    { CommandEntry }
  1361.  
  1362. END.
  1363.